/*
 * DimensionalValue.java
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs.physics;

import java.math.BigDecimal;
import us.desertlabs.number.BigUtility;

/**
 * Used to define a specific set of numerical values with some arbitrary
 * dimensionality.
 *
 * @author Brandon Franklin
 */
public class DimensionalValue {

    /** The individual component values of this <CODE>DimensionalValue</CODE> */
    final private Number[] values;

    /**
     * Creates a new instance of <CODE>DimensionalValue</CODE> with the
     * supplied component values.
     *
     * @param values the individual component values in each dimension
     * @throws IllegalArgumentException if any of the parameters are invalid
     */
    public DimensionalValue( final Number[] values ) {

        // Validate the values
        if( values == null ) {
            throw new IllegalArgumentException( "Values cannot be null." );
        }

        // Store a copy of the value array
        this.values = new Number[values.length];
        System.arraycopy( values, 0, this.values, 0, this.values.length );

        // Do a final check for nulls
        if( !containsNoNulls() ) {
            throw new IllegalArgumentException( "Values provided contain at least one null value." );
        }

    }

    /**
     * Returns the individual values of this <CODE>DimensionalValue</CODE> in
     * every dimension.
     *
     * @return the individual values of this <CODE>DimensionalValue</CODE> in
     *         every dimension
     */
    public Number[] getValues() {

        final Number[] valuesCopy = new Number[values.length];
        System.arraycopy( values, 0, valuesCopy, 0, valuesCopy.length );

        return valuesCopy;
    }

    /**
     * Returns the combined value obtained by adding all of the individual
     * values from every dimension together. The vals will all be converted to
     * their own absolute values, so the aggregate will always reflect the total
     * "amplitude" of the values, not just their arithmetic sum.
     *
     * @return the combined value obtained by adding all of the individual
     *         values from every dimension together
     */
    public Number getAggregateValue() {

        final Number[] vals = getValues();
        final BigDecimal total = new BigDecimal( vals[0].toString() ).abs();

        for( int i = 1; i < vals.length; i++ ) {
            total.add( new BigDecimal( vals[i].toString() ).abs() );
        }

        return BigUtility.clipScale( total );
    }

    /**
     * Returns the number of dimensions in this <CODE>DimensionalValue</CODE>.
     * This is the number of <CODE>Number</CODE>s that were defined when the
     * <CODE>DimensionalValue</CODE> was instantiated.
     *
     * @return the number of dimensions in this <CODE>DimensionalValue</CODE>
     */
    public int getDimensionality() {
        return values.length;
    }

    /**
     * Returns true if there are no null values in the value array, false if
     * there are any.
     *
     * @return true if there are no null values in the value array, false if
     *         there are any
     */
    private boolean containsNoNulls() {
        for( Number value : getValues() ) {
            if( value == null ) {
                return false;
            }
        }

        return true;
    }

    /**
     * Checks whether or not this <CODE>DimensionalValue</CODE> is currently
     * valid and returns true if so, false otherwise.
     *
     * @return true if this <CODE>DimensionalValue</CODE> is currently valid,
     *         false otherwise
     */
    public boolean isValid() {
        return containsNoNulls();
    }

    /**
     * Returns a <CODE>String</CODE> representation of this <CODE>DimensionalValue</CODE>.
     *
     * @return a <CODE>String</CODE> representation of this <CODE>DimensionalValue</CODE>
     */
    @Override
    public String toString() {

        final StringBuilder sb = new StringBuilder();

        sb.append( getClass().getName() );
        sb.append( "[ " );

        for( int i = 0; i < values.length; i++ ) {
            sb.append( values[i].toString() );
            if( i < values.length - 1 ) {
                sb.append( ", " );
            }
        }

        sb.append( " ]" );

        return sb.toString();
    }
}
